package cn.bearspark.api.service.main.impl;

import cn.bearspark.api.service.main.CommentService;
import cn.bearspark.api.util.ParseableDataUtil;
import cn.bearspark.api.vo.HtmlTextUnparseable;
import cn.bearspark.api.vo.main.comment.*;
import cn.bearspark.api.vo.main.user.UserSlimVO;
import cn.bearspark.article.rpc.ArticleRpc;
import cn.bearspark.comment.dto.*;
import cn.bearspark.common.constant.GlobalConstant;
import cn.bearspark.common.enumeration.ModuleEnum;
import cn.bearspark.comment.rpc.CommentRpc;
import cn.bearspark.common.dto.Page;
import cn.bearspark.common.util.ConvertBeanUtil;
import cn.bearspark.emoji.rpc.EmojiRpc;
import cn.bearspark.framework.web.starter.context.BSRequestContext;
import cn.bearspark.happening.rpc.HappeningRpc;
import cn.bearspark.common.enumeration.LikedEnum;
import cn.bearspark.like.rpc.LikeRpc;
import cn.bearspark.user.dto.UserSlimDTO;
import cn.bearspark.user.rpc.UserAtRpc;
import cn.bearspark.user.rpc.UserBaseRpc;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author f
 */
@Service
public class CommentServiceImpl implements CommentService {

    @DubboReference(check = GlobalConstant.DUBBO_CHECK)
    private CommentRpc commentRpc;
    @DubboReference(check = GlobalConstant.DUBBO_CHECK)
    private UserBaseRpc userBaseRpc;
    @DubboReference(check = GlobalConstant.DUBBO_CHECK)
    private HappeningRpc happeningRpc;
    @DubboReference(check = GlobalConstant.DUBBO_CHECK)
    private LikeRpc likeRpc;
    @DubboReference(check = GlobalConstant.DUBBO_CHECK)
    private UserAtRpc userAtRpc;
    @DubboReference(check = GlobalConstant.DUBBO_CHECK)
    private EmojiRpc emojiRpc;
    @DubboReference(check = GlobalConstant.DUBBO_CHECK)
    private ArticleRpc articleRpc;

    private final ParseableDataUtil parseableDataUtil;

    public CommentServiceImpl(ParseableDataUtil parseableDataUtil) {
        this.parseableDataUtil = parseableDataUtil;
    }

    @Override
    public BldgPageVO pageQueryHappening(Integer pageNum, Integer pageSize, Long happeningId, Integer order) {
        long userId = BSRequestContext.getUserId();
        Page<BldgCommentDTO> bldgCommentDTOPage = commentRpc.pageQueryHappening(pageNum, pageSize, happeningId, order);

        BldgPageVO bldgPageVO = new BldgPageVO();
        bldgPageVO.setTotal(happeningRpc.getCommentNum(happeningId));
        bldgPageVO.setBldgTotal(bldgCommentDTOPage.getTotal());
        bldgPageVO.setBldgCommentList(asmCommentVOList(
                bldgCommentDTOPage.getRecords(),
                BldgCommentDTO::getCommentId,
                BldgCommentDTO::getUserId,
                BldgCommentVO::setUserInfo,
                BldgCommentVO::setLiked,
                BldgCommentVO.class,
                userId
        ));
        return bldgPageVO;
    }

    @Override
    public LayerPageVO pageQueryLayer(Integer pageNum, Integer pageSize, Long commentId) {
        long userId = BSRequestContext.getUserId();
        List<LayerCommentDTO> layerCommentDTOList = commentRpc.pageQueryLayer(pageNum, pageSize, commentId);

        List<LayerCommentVO> layerCommentList = asmCommentVOList(
                layerCommentDTOList,
                LayerCommentDTO::getCommentId,
                LayerCommentDTO::getUserId,
                LayerCommentVO::setUserInfo,
                LayerCommentVO::setLiked,
                LayerCommentVO.class,
                userId
        );

        // 设置每条层评论回复评论的用户名
        Map<Long, String> usernameMap = userBaseRpc.mapUsername(layerCommentList.stream()
                .map(LayerCommentVO::getRepliedUserId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet()));
        layerCommentList.forEach(layerCommentVO ->
                layerCommentVO.setRepliedUserName(usernameMap.get(layerCommentVO.getRepliedUserId())));

        LayerPageVO layerPageVO = new LayerPageVO();
        layerPageVO.setLayerCommentList(layerCommentList);
        return layerPageVO;
    }

    // 组装评论 VO 对象集合：查询该用户是否点赞过评论，填充用户最简信息，将 DTO 转化成 VO
    private <VO extends HtmlTextUnparseable, DTO> List<VO> asmCommentVOList(
            List<DTO> dtoList,
            Function<DTO, Long> dtoIdGetter,
            Function<DTO, Long> dtoUserIdGetter,
            BiConsumer<VO, UserSlimVO> voUserInfoSetter,
            BiConsumer<VO, Integer> voLikedSetter,
            Class<VO> voClazz,
            long userId) {
        List<Long> commentIdList = new ArrayList<>(dtoList.size());
        Set<Long> userIdSet = new HashSet<>();
        dtoList.forEach(dto -> {
            commentIdList.add(dtoIdGetter.apply(dto));
            userIdSet.add(dtoUserIdGetter.apply(dto));
        });

        // 查询用户是否点赞过这些评论
        Set<Long> likedCommentIdSet = !commentIdList.isEmpty() ?
                likeRpc.getLikedCommentIdSet(commentIdList, userId) :
                new HashSet<>();

        // 查询发布评论的用户最简信息
        Map<Long, UserSlimDTO> userSlimDTOMap = !CollectionUtils.isEmpty(userIdSet) ?
                userBaseRpc.mapSlimInfo(userIdSet) :
                new HashMap<>();

        // 填充用户最简信息，将 DTO 转化成 VO
        List<VO> voList = dtoList.stream().map(dto -> {
            VO vo = ConvertBeanUtil.convert(dto, voClazz);
            voUserInfoSetter.accept(vo, ConvertBeanUtil.convert(
                    userSlimDTOMap.get(dtoUserIdGetter.apply(dto)), UserSlimVO.class));
            voLikedSetter.accept(vo, likedCommentIdSet.contains(dtoIdGetter.apply(dto)) ?
                    LikedEnum.LIKED.getCode() :
                    LikedEnum.UNLIKED.getCode());
            return vo;
        }).toList();

        // 反解析所有的 content，设置必需的信息
        parseableDataUtil.unparse(ModuleEnum.COMMENT.getCode(), voList);

        return voList;
    }

    @Override
    public void commentHappening(HappeningReqVO happeningReqVO) {
        // 判断动态是否可评论，如果不可评论，则无需评论
        long happeningId = happeningReqVO.getHappeningId();
        if (!happeningRpc.isCommentable(happeningId)) {
            return;
        }

        long userId = BSRequestContext.getUserId();

        // 解析并发布
        HappeningReqDTO happeningReqDTO = ConvertBeanUtil.convert(happeningReqVO, HappeningReqDTO.class);
        happeningReqDTO.setUserId(userId);
        parseableDataUtil.publish(ModuleEnum.COMMENT.getCode(),
                () -> commentRpc.commentHappening(happeningReqDTO), happeningReqDTO);

        // 增加动态的评论数
        happeningRpc.incrCommentNum(happeningId);
    }

    @Override
    public void replyComment(ReplyReqVO replyReqVO) {
        long userId = BSRequestContext.getUserId();

        // 解析并发布
        ReplyReqDTO replyReqDTO = ConvertBeanUtil.convert(replyReqVO, ReplyReqDTO.class);
        replyReqDTO.setUserId(userId);
        AtomicReference<ReplyRespDTO> replyRespDTORef = new AtomicReference<>();
        parseableDataUtil.publish(ModuleEnum.COMMENT.getCode(), () -> {
            ReplyRespDTO replyRespDTO = commentRpc.reply(replyReqDTO);
            replyRespDTORef.set(replyRespDTO);
            return replyRespDTO.getReplyCommentId();
        }, replyReqDTO);

        // 如果没有回复，则直接返回即可
        ReplyRespDTO replyRespDTO = replyRespDTORef.get();
        if (replyRespDTO == null) {
            return;
        }

        // 增加动态的评论数
        happeningRpc.incrCommentNum(replyRespDTO.getHappeningId());
    }

    @Override
    public void delete(Long commentId) {
        long userId = BSRequestContext.getUserId();
        DeleteRespDTO deleteRespDTO = commentRpc.delete(userId, commentId);

        // 如果没有删除，则直接返回即可
        if (deleteRespDTO == null) {
            return;
        }

        // 删除 评论带有的其它数据
        parseableDataUtil.afterDeleteBase(ModuleEnum.COMMENT.getCode(), commentId);

        // 减少动态的评论数
        happeningRpc.decrCommentNum(deleteRespDTO.getHappeningId(), deleteRespDTO.getDeleted());
    }
}
